#include "patterneditor.h"
#include <boost/bind.hpp>
#include "song.h"
#include "track.h"
#include "songitem.h"
#include "pattern.h"
#include "event.h"
#include "instrument.h"
using namespace boost;
using namespace std;

PatternEditor::PatternEditor(Song *pSong) 
: m_pSong(NULL), m_pTrack(NULL), m_pSongItem(NULL), 
  m_pPattern(NULL), m_pEvent(NULL),
  m_curTrackNum(PatternEditor::NoTrack), 
  m_curSongItemNum(PatternEditor::NoSongItem), 
  m_curRowNum(PatternEditor::NoRow), 
  m_curField(PatternEditor::NoField), m_octave(4), m_instNum(0),
  m_itemField(PatternEditor::Number0) {
     setSong(pSong);
}

PatternEditor::~PatternEditor() {
}

void PatternEditor::reset() {
     setCurSongItemNum(0);
     setCurTrackNum(0);
     setCurRowNum(0);
     setFirstField();
}

Song* PatternEditor::song() {
     return m_pSong;
}

void PatternEditor::setSong(Song *pSong) {
     m_pSong = pSong;
     if (!m_pSong)
	  return;
     reset();
}

int PatternEditor::curTrackNum() {
     return m_curTrackNum;
}

void PatternEditor::setCurTrackNum(int num) {
     if (!m_pSong || !m_pSong->trackCount()) {
	  m_curTrackNum = PatternEditor::NoTrack;
	  m_pTrack = NULL;
	  return;
     } else {
	  if (num < 0)
	       m_curTrackNum = 0;
	  else if (num >= m_pSong->trackCount())
	       m_curTrackNum = m_pSong->trackCount() - 1;
	  else
	       m_curTrackNum = num;
	  m_pTrack = m_pSong->trackAt(m_curTrackNum);
     }
     // call this with current value to make sure it's still in range
     setCurSongItemNum(m_curSongItemNum);
}

Track* PatternEditor::curTrack() {
     return m_pTrack;
}

int PatternEditor::curSongItemNum() {
     return m_curSongItemNum;
}

void PatternEditor::setCurSongItemNum(int num) {
     if (!m_pTrack || !m_pTrack->songItemCount()) {
	  m_curSongItemNum = PatternEditor::NoSongItem;
	  m_pSongItem = NULL;
	  m_pPattern = NULL;
     } else {
	  if (num < 0)
	       m_curSongItemNum = 0;
	  else if (num >= m_pTrack->songItemCount())
	       m_curSongItemNum = m_pTrack->songItemCount() - 1;
	  else
	       m_curSongItemNum = num;
	  m_pSongItem = m_pTrack->songItemAt(m_curSongItemNum);
	  m_pPattern = m_pSongItem->pattern();
     }
     // call this with current value to make sure it's still in range
     setCurRowNum(m_curRowNum);
//     setFirstField();
}

int PatternEditor::curPatternNum() {
     if (!m_pPattern)
	  return -1;
     return m_pPattern->number();
}

void PatternEditor::setCurPatternNum(int num) {
     // create patterns if necessary
     if (num >= m_pSong->patternCount())
	  m_pSong->setPatternCount(num + 1);
     m_pPattern = m_pTrack->patternAt(num);
     if (!m_pPattern || !m_pSongItem)
	  return;
     m_pSongItem->setPattern(m_pPattern);
     setCurRowNum(0);
     setFirstField();
}

Pattern* PatternEditor::curPattern() {
     return m_pPattern;
}

int PatternEditor::curRowNum() {
     return m_curRowNum;
}

void PatternEditor::setCurRowNum(int num) {
     if (!m_pPattern || !m_pPattern->length()) {
	  m_curRowNum = PatternEditor::NoRow;
	  m_pEvent = NULL;
     } else {
	  if (num < 0)
	       m_curRowNum = 0;
	  else if (num >= m_pPattern->length())
	       m_curRowNum = m_pPattern->length() - 1;
	  else
	       m_curRowNum = num;
	  m_pEvent = m_pPattern->eventAt(m_curRowNum);
     }
     // call this with current value to make sure it's still in range
     setCurField(m_curField);
}

PatternEditor::Field PatternEditor::curField() {
     return m_curField;
}

void PatternEditor::setCurField(PatternEditor::Field field) {
     if (!m_pEvent) {
	  m_curField = PatternEditor::NoField;
     } else {
	  if (m_pEvent->type() == Event::Note && 
	      (field == PatternEditor::CCNum0 || 
	       field == PatternEditor::CCNum1))
	       m_curField = PatternEditor::Note;
	  else if (m_pEvent->type() == Event::ControlChange && 
		   field == PatternEditor::Note)
	       m_curField = PatternEditor::CCNum0;
	  else if (field == PatternEditor::NoField)
	       setFirstField();
	  else
	       m_curField = field;
     }
}

void PatternEditor::incField() {
     switch (m_curField) {
     case PatternEditor::Note:
	  setCurField(PatternEditor::Instrument0);
	  break;
     case PatternEditor::CCNum0:
	  setCurField(PatternEditor::CCNum1);
	  break;
     case PatternEditor::CCNum1:
	  setCurField(PatternEditor::Instrument0);
	  break;
     case PatternEditor::Instrument0:
	  setCurField(PatternEditor::Instrument1);
	  break;
     case PatternEditor::Instrument1:
	  setCurField(PatternEditor::Val0);
	  break;
     case PatternEditor::Val0:
	  setCurField(PatternEditor::Val1);
	  break;
     case PatternEditor::Val1:
	  setCurField(PatternEditor::Effect);
	  break;
     case PatternEditor::Effect:
	  setCurField(PatternEditor::EffectArg0);
	  break;
     case PatternEditor::EffectArg0:
	  setCurField(PatternEditor::EffectArg1);
	  break;
     case PatternEditor::EffectArg1:
	  if (m_curTrackNum < m_pSong->trackCount() - 1) {
	       setCurTrackNum(m_curTrackNum + 1);
	       setFirstField();
	  }
	  break;
     case PatternEditor::NoField:
	  break;
     }
}

void PatternEditor::decField() {
     switch (m_curField) {
     case PatternEditor::EffectArg1:
	  setCurField(PatternEditor::EffectArg0);
	  break;
     case PatternEditor::EffectArg0:
	  setCurField(PatternEditor::Effect);
	  break;
     case PatternEditor::Effect:
	  setCurField(PatternEditor::Val1);
	  break;
     case PatternEditor::Val1:
	  setCurField(PatternEditor::Val0);
	  break;
     case PatternEditor::Val0:
	  setCurField(PatternEditor::Instrument1);
	  break;
     case PatternEditor::Instrument1:
	  setCurField(PatternEditor::Instrument0);
	  break;
     case PatternEditor::Instrument0:
	  if (m_pEvent->type() == Event::Note)
	       setCurField(PatternEditor::Note);
	  else
	       setCurField(PatternEditor::CCNum1);
	  break;
     case PatternEditor::Note:
	  if (m_curTrackNum > 0) {	  
	       setCurTrackNum(m_curTrackNum - 1);
	       setCurField(PatternEditor::EffectArg1);
	  }
	  break;
     case PatternEditor::CCNum1:
	  setCurField(PatternEditor::CCNum0);
	  break;
     case PatternEditor::CCNum0:
	  if (m_curTrackNum > 0) {	  
	       setCurTrackNum(m_curTrackNum - 1);
	       setCurField(PatternEditor::EffectArg1);
	  }
	  break;
     case PatternEditor::NoField:
	  break;
     }
}

void PatternEditor::setFirstField() {
     if (!m_pEvent) {
	  m_curField = PatternEditor::NoField;
	  return;
     }
     if (m_pEvent->type() == Event::Note)
	  m_curField = PatternEditor::Note;
     else
	  m_curField = PatternEditor::CCNum0;
}

Event* PatternEditor::curEvent() {
     return m_pEvent;
}

SongItem* PatternEditor::curSongItem() {
     return m_pSongItem;
}

void PatternEditor::toggleCurEventType() {
     if (!m_pEvent)
	  return;
     m_pEvent->toggleType();
     if (m_pEvent->type() == Event::ControlChange) {
	  if (m_pEvent->instNum() == Event::NoInst)
	       m_pEvent->setInstNum(m_instNum);
	  m_pEvent->setVal(64);
     }
     if (m_curField == PatternEditor::Note)
	  m_curField = PatternEditor::CCNum0;
     else if (m_curField == PatternEditor::CCNum0 ||
	      m_curField == PatternEditor::CCNum1)
	  m_curField = PatternEditor::Note;
}

int PatternEditor::octave() {
     return m_octave;
}

void PatternEditor::setOctave(int octave) {
     m_octave = octave;
}

int PatternEditor::curInstNum() {
     return m_instNum;
}

void PatternEditor::setCurInstNum(int num) {
     m_instNum = num;
     // emit onInstChange signal
     m_onInstChange(m_instNum);
}

int PatternEditor::curItemField() {
     return m_itemField;
}

void PatternEditor::setCurItemField(int field) {
     m_itemField = field;
}

void PatternEditor::incItemField() {
     if (m_itemField < SongItem::strlen - 1)
	  m_itemField++;
     else if (m_curTrackNum < m_pSong->trackCount() - 1) {
	  setCurTrackNum(m_curTrackNum + 1);
	  setCurItemField(PatternEditor::Number0);
     }
}

void PatternEditor::decItemField() {
     if (m_itemField > 0)
	  m_itemField--;
     else if (m_curTrackNum > 0) {	  
	  setCurTrackNum(m_curTrackNum - 1);
	  setCurItemField(SongItem::strlen - 1);
     }
}

signals2::connection PatternEditor::onInstChange(const OnInstChange::slot_type &callback) {
     return m_onInstChange.connect(callback);
}

void PatternEditor::setCurInstName(const string &name) {
     if (!m_pSong->instrumentCount())
	  return;
     Instrument *pInst = m_pSong->instrumentAt(curInstNum());
     assert(pInst);
     pInst->setName(name);
}
